Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

put __super__ on the prototype #787

Closed
wants to merge 1 commit into from

Conversation

tbranyen
Copy link
Collaborator

Been doing research on extending Backbone and found this interesting bit:

// Set a convenience property in case the parent's prototype is needed later.
child.__super__ = parent.prototype;

At an initial glance it looks as if it could work this this.__super__.someMethod, but in reality you must call it off the constructor which is assigned directly above.

This patch instead places __super__ on the prototype.

/* One level of extension, calling Backbone.Model.prototype.initialize */

// Current
this.constructor.__super__.initialize.call(this);

// New
this.__super__.initialize.call(this);

// Manually
Backbone.Model.prototype.initialize.call(this);

I figure there must be an intentional reason for why its currently set up the way it is. Any elaboration would be great.

@jashkenas
Copy link
Owner

Yes __super__ is not intended for you to use it directly, as the underscored name implies.

It's the internal property that CoffeeScript uses to make super() work efficiently, and should not be an enumerable property on the prototype. In JavaScript, you should use the manual style:

Backbone.Model.prototype.initialize.call(this);

The existence of __super__ in Backbone is our one line concession so that you can write:

Model = Backbone.Model.extend

  set: (attrs, options) ->
    super

... and have it work.

@jashkenas jashkenas closed this Dec 14, 2011
@tbranyen
Copy link
Collaborator Author

One thing I was thinking of to be fair to the JavaScript users is doing:

child.prototype.super = parent.prototype;

So the JavaScript'ers could do:

this.super.initialize.call(this);

@jashkenas
Copy link
Owner

Unfortunately, Backbone isn't trying to be a JavaScript OOP library ... we're not providing "special" super() semantics in general. This means that in JavaScript, you should be using the standard pattern:

Parent.prototype.method.call(this, arg);

If we did want to be an OOP library -- we would be providing much more sugar than this.

@yuchi
Copy link

yuchi commented Dec 14, 2011

I propose the creation of a wiki page on CoffeeScript which targets the "How to write coffeescript-compatible libraries in Javascript"

@jashkenas
Copy link
Owner

Yes, that would make a fine wiki page, but let's be clear: Every JavaScript library is compatible with CoffeeScript -- this is simply a convenience so that super() can be used, even when you're not using the class keyword (which you could also use). A special case.

@blocka
Copy link

blocka commented Dec 14, 2011

Funny, I never fully understood prototypical inheritance until I used Backbone...but I see people are still running away into their comfort zone.

@ehynds
Copy link

ehynds commented Dec 14, 2011

+1. Why not give regular JS users the convenience instead of just CS users? Part of backbone's elegance is extending views, collections, etc. and calling the "super" method is part of the territory.

@yuchi
Copy link

yuchi commented Dec 14, 2011

While @jashkenas said

Yes __super__ is not intended for you to use it directly, as the underscored name implies.

IMHO it's a super legit use of __super__. But that should be used only when you don't know what your super class is. If you know it you should call it directly.

@philfreo
Copy link
Contributor

Just my two cents: "no fair" that Backbone adds special code to support super in CoffeeScript but not regular JavaScript when both could be just as easily done. It's cool that Backbone doesn't want to add lots of OOP support, but supporting this one minor thing in JS as well as CS would be much appreciated. Otherwise having an object that extends multiple methods is error-prone and tedious to refactor when the parent class name changes.

@tonyxiao
Copy link

Model = Backbone.Model.extend

  set: (attrs, options) ->
    super

Doesn't actually work with coffeeScript, it will output the following javascript, which will throw a reference error on set is not defined

var Model;
Model = Backbone.Model.extend({
  set: function(attrs, options) {
    return set.__super__.constructor.call(this, doesntworkactually);
  }
});

@braddunbar
Copy link
Collaborator

@tonyxiao I believe what @jashkenas meant was something like the following...?

Model = Bakbone.Model.extend()
Model::set = ->
  # ...
  super

which compiles to

var Model;

Model = Backbone.Model.extend();

Model.prototype.set = function() {
  return Model.__super__.set.apply(this, arguments);
};

@yuchi
Copy link

yuchi commented Jun 28, 2012

What about

class Model extends Backbone.Model
  set: ->
    #...
    super

@theefer
Copy link

theefer commented Jul 5, 2012

+1

I ended up implementing an invokeSuper() method yesterday which can be used from Backbone hierarchies of classes.

SomeModelClass.extend({
  method: function() {
    this.invokeSuper(arguments);
    // do something
  }
})

It's usable as a mixin, see the code here: https://gist.github.com/3054503

@dstibrany
Copy link

After reading a few articles on this subject, I ended up going with something like this

(function(Backbone, _) {

    // Extend Backbone Classes with a 'super' function to execute a method of an instance's superclass
    _.each(['Collection', 'Model', 'View', 'Router'], function(className) {
        Backbone[className].prototype.super = function(funcName) {
            var parentPrototype = this.constructor.__super__;
            if (parentPrototype && typeof parentPrototype[funcName] === 'function') {
                return this.constructor.__super__[funcName].apply(this, _.rest(arguments));
            }
        };
    });

})(Backbone, _);

And then invoking it like:

var Account = SomeCustomCollection.extend({
      initialize: function() {
          this.super('initialize');
      }
});

Sucks that you have to pass in the method name as an argument, but I like it otherwise.

@sgreenfield
Copy link

For those of use wanting this bit of OOP sugar, this looks promising: https://github.com/lukasolson/Backbone-Super

@moos
Copy link

moos commented Feb 28, 2013

@dstibrany: good concise code, but YUI compressor fails because of 'super'. Using '_super' works.

Backbone[className].prototype._super = function(funcName) { ...

@yuchi
Copy link

yuchi commented Mar 2, 2013

No guys, it's not a good idea to use such a "super" method.

Let me expose it:

var Root = Backbone.Model.extend({
  log: function (msg) {
   console.log(msg);
  }
});

var Screamer = Root.extend({
  log: function (msg) {
    this.constructor.__super__.log.call(this, msg + '!');
  }
});

var SuperScreamer = Screamer.extend({
  log: function (msg) {
    this.constructor.__super__.log.call(this, 'OH MY GOD, '+msg);
  }
});

var aSuperScreamer = new SuperScreamer();

aSuperScreamer.log('Hallo world'); // ERROR: Maximum call stack exceeded.

Why? Because Screamer.prototype.log calls this.constructor.__super__ which is Screamer.prototype ;)

@mehcode
Copy link

mehcode commented Mar 2, 2013

Just for clarification, if you want to use __super__, use it as follows:

SuperScreamer.__super__.log.call(this, 'OH MY GOD, '+msg);

See http://jsfiddle.net/zEhYZ/

@yuchi
Copy link

yuchi commented Mar 2, 2013

Sorry, I forgot to put the good pattern alongside!

@trusktr
Copy link

trusktr commented Jan 26, 2015

ES6 Classes are almost out, bringing the built-in ability to call super() inside a class method. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet